const boom = require("boom");

const axios = require("axios");

const User = require("../models/common");
const Role = require("../models/role");
const Api = require("../models/api");
const JWTToken = require("../models/token");
const Person = require("../models/person");

const fs = require("fs");
const Qrcode = require("qrcode");
const path = require("path");

const bcrypt = require("bcrypt");

const { generateToken, verifyToken } = require("./../utils/authorization");

exports.WXminLogin = async (req, reply) => {
  try {
    const { appid, secret, code } = req.body;

    if (!appid || !secret || !code) {
      throw boom.badRequest("appid,secret,code不能为空");
    }

    const tokenResponse = await axios({
      method: "get",
      url: "https://api.weixin.qq.com/cgi-bin/token",
      headers: {
        "Content-Type": "application/json",
      },
      params: {
        grant_type: "client_credential",
        appid: appid,
        secret: secret,
      },
    });

    const phoneResponse = await axios({
      method: "post",
      url: `https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=${tokenResponse.data.access_token}`,
      data: {
        code: code,
      },
    });

    const phoneNumber = phoneResponse.data.phone_info.purePhoneNumber;

    // 查询数据库中是否已经有该手机号的记录
    const existingUser = await User.findOne({ phone: phoneNumber });

    if (!existingUser) {
      // 如果没有找到，则创建新的用户记录
      const newUser = new User({
        phone: phoneNumber,
      });
      await newUser.save();

      const token = jwt.sign({ phone: phoneNumber }, "shannnon", { expiresIn: "2h" });
      return reply.send({ token });
    } else {
      const token = jwt.sign({ phone: phoneNumber }, "shannnon", { expiresIn: "2h" });
      return reply.send({ token });
    }
  } catch (error) {
    throw boom.boomify(error);
  }
};

exports.Login = async (req, reply) => {
  try {
    const { account, password } = req.body;

    if (!account || !password) {
      throw boom.badRequest("account,password不能为空");
    }

    // 查找用户
    const user = await User.findOne({ account });

    if (!user) {
      throw boom.unauthorized("用户不存在或密码错误");
    }

    // 验证密码
    const isValid = await bcrypt.compare(password, user.password);

    if (!isValid) {
      throw boom.unauthorized("用户不存在或密码错误");
    }

    let payload = {
      phone: user.phone,
      account: user.account,
    };

    const newToken = generateToken(payload);

    // 查询所有记录，并只获取 token 字段
    const tokensWithId = await JWTToken.find({}, "token");

    // 映射处理以移除 _id 字段，仅保留 token
    const tokens = tokensWithId.map((tokenObj) => tokenObj.token);

    // 使用 Promise.all 同时验证所有 token，并且在结果中保留 token
    const promises = tokens.map(async (token) => {
      const verificationResult = await verifyToken(token);
      return { ...verificationResult, token }; // 这里将 token 添加到验证结果对象中
    });

    const results = await Promise.all(promises);

    // 提取包含 phone 和 token 的列表
    const phonelist = results.map((result) => ({ phone: result.phone, token: result.token }));

    // 查找包含特定电话号码的对象
    const foundItem = phonelist.find((item) => item.phone === user.phone);

    if (foundItem == undefined) {
      await User.findOneAndUpdate({ account }, { status: true }, { new: true });
      const createToken = new JWTToken({ token: newToken });
      await createToken.save();
    } else {
      if (phonelist.some((item) => item.phone == user.phone)) {
        await User.findOneAndUpdate({ account }, { status: true }, { new: true });
        await JWTToken.findOneAndUpdate({ token: foundItem.token }, { token: newToken }, { new: true });
      }
    }

    return reply.send({
      message: "登录成功",
      token: newToken,
    });
  } catch (error) {
    throw boom.boomify(error);
  }
};

exports.PhoneLogin = async (req, reply) => {
  const { phone, code } = req.body;

  if (!phone || !code) {
    throw boom.badRequest("phone,code不能为空");
  }

  // 查找用户
  const user = await User.findOne({ phone });
  if (!user) {
    throw boom.badRequest("用户不存在");
  }
  // 验证验证码
  console.log(user.code);
  if (user.code !== code) {
    throw boom.badRequest("验证码错误");
  }

  // 验证验证码是否过期
  const now = new Date();
  if (now >= user.codeExpiresAt) {
    // 重置验证码
    await User.findOneAndUpdate({ phone }, { code: "", codeExpiresAt: "" });
    throw boom.badRequest("验证码已过期");
  }

  let payload = {
    phone: user.phone,
    account: user.account,
  };

  const newToken = generateToken(payload);

  // 查询所有记录，并只获取 token 字段
  const tokensWithId = await JWTToken.find({}, "token");

  // 映射处理以移除 _id 字段，仅保留 token
  const tokens = tokensWithId.map((tokenObj) => tokenObj.token);

  // 使用 Promise.all 同时验证所有 token，并且在结果中保留 token
  const promises = tokens.map(async (token) => {
    const verificationResult = await verifyToken(token);
    return { ...verificationResult, token }; // 这里将 token 添加到验证结果对象中
  });

  const results = await Promise.all(promises);

  // 提取包含 phone 和 token 的列表
  const phonelist = results.map((result) => ({ phone: result.phone, token: result.token }));

  // 查找包含特定电话号码的对象
  const foundItem = phonelist.find((item) => item.phone === user.phone);

  if (foundItem == undefined) {
    await User.findOneAndUpdate({ phone }, { status: true }, { new: true });
    const createToken = new JWTToken({ token: newToken });
    await createToken.save();
  } else {
    if (phonelist.some((item) => item.phone == user.phone)) {
      await User.findOneAndUpdate({ phone }, { status: true }, { new: true });
      await JWTToken.findOneAndUpdate({ token: foundItem.token }, { token: newToken }, { new: true });
    }
  }

  reply.send({
    message: "登录成功",
    token: newToken,
  });
};

exports.Registration = async (req, reply) => {
  try {
    const { phone, code, account, password } = req.body;

    if (!phone || !code || !account || !password) {
      throw boom.badRequest("phone,code,account,password不能为空");
    }

    const existingUser = await User.findOne({ phone });
    if (existingUser) {
      throw boom.badRequest("该手机号已存在");
    }

    const existingAccount = await User.findOne({ account });
    if (existingAccount) {
      throw boom.badRequest("该账户已存在");
    }

    // 正则校验手机号是否正确
    const phoneReg = /^1[34578]\d{9}$/;
    if (!phoneReg.test(phone)) {
      throw boom.badRequest("手机号格式不正确");
    }

    // 使用 bcrypt 对密码进行加密
    const hashedPassword = await bcrypt.hash(password, 10); // 第二个参数是 salt 的工作因子

    const newUser = new User({
      phone,
      account,
      password: hashedPassword,
      status: false,
    });

    await newUser.save();

    reply.send({ message: "注册成功" });
  } catch (error) {
    throw boom.boomify(error);
  }
};

exports.Unsubscribe = async (req, reply) => {
  try {
    const { phone, code, confirm } = req.body;
    if (!phone || !code) {
      throw boom.badRequest("phone,code不能为空");
    }

    // 查找用户
    const user = await User.findOne({ phone });
    if (!user) {
      throw boom.badRequest("用户不存在");
    }

    // 验证验证码
    if (user.code !== code) {
      throw boom.badRequest("验证码错误");
    }

    if (!confirm) {
      throw boom.badRequest("是否确认注销账户");
    }

    // 验证验证码是否过期
    const now = new Date();
    if (now >= user.codeExpiresAt) {
      // 重置验证码
      await User.findOneAndUpdate({ phone }, { code: "", codeExpiresAt: "" });
      throw boom.badRequest("验证码已过期");
    }

    await User.findOneAndDelete({ phone });
  } catch (error) {
    throw boom.boomify(error);
  }
};

exports.LoginOut = async (req, reply) => {
  try {
    const { headers } = req;

    const existingToken = await JWTToken.findOne({ token: headers.token });
    if (!existingToken) {
      throw boom.unauthorized("Token 无效");
    }

    await JWTToken.findOneAndDelete({ token: headers.token });
    return reply.send({ message: "退出成功" });
  } catch (error) {
    throw boom.boomify(error);
  }
};

exports.RetrievePassword = async (req, reply) => {
  try {
    const { phone, code, newPassword, confirm } = req.body;
    if (!phone || !code || !newPassword) {
      throw boom.badRequest("phone,code,newPassword不能为空");
    }

    if (!confirm) {
      throw boom.badRequest("是否确认新密码");
    }

    // 查找用户
    const user = await User.findOne({ phone });
    if (!user) {
      throw boom.badRequest("用户不存在");
    }

    // 验证验证码
    if (user.code !== code) {
      throw boom.badRequest("验证码错误");
    }

    // 验证验证码是否过期
    const now = new Date();
    if (now >= user.codeExpiresAt) {
      // 重置验证码
      await User.findOneAndUpdate({ phone }, { code: "", codeExpiresAt: "" });
      throw boom.badRequest("验证码已过期");
    }

    // 确保新密码与旧密码不同
    const isSamePassword = await bcrypt.compare(newPassword, user.password);
    if (isSamePassword) {
      throw boom.badRequest("新密码不能与旧密码相同");
    }

    // 使用 bcrypt 对密码进行加密
    const hashedPassword = await bcrypt.hash(newPassword, 10); // 第二个参数是 salt 的工作因子

    if (user.status) {
      // 查询所有记录，并只获取 token 字段
      const tokensWithId = await JWTToken.find({}, "token");

      // 映射处理以移除 _id 字段，仅保留 token
      const tokens = tokensWithId.map((tokenObj) => tokenObj.token);

      // 使用 Promise.all 同时验证所有 token，并且在结果中保留 token
      const promises = tokens.map(async (token) => {
        const verificationResult = await verifyToken(token);
        return { ...verificationResult, token }; // 这里将 token 添加到验证结果对象中
      });

      const results = await Promise.all(promises);

      // 提取包含 phone 和 token 的列表
      const phonelist = results.map((result) => ({ phone: result.phone, token: result.token }));

      // 查找包含特定电话号码的对象
      const foundItem = phonelist.find((item) => item.phone === user.phone);
      if (foundItem) {
        await JWTToken.findOneAndDelete({ token: foundItem.token });
      }
    }

    // 更新密码
    await User.findOneAndUpdate({ phone }, { password: hashedPassword, code: "", codeExpiresAt: "" });

    reply.send({ message: "修改密码成功" });
  } catch (error) {
    throw boom.boomify(error);
  }
};

function generateRandomCode() {
  let code = "";
  for (let i = 0; i < 6; i++) {
    // 生成0到9之间的随机数字
    const digit = Math.floor(Math.random() * 10);
    code += digit;
  }
  return code;
}

function timestampToDate(timestamp) {
  const date = new Date(timestamp);
  const year = date.getFullYear();
  const month = String(date.getMonth() + 1).padStart(2, "0"); // 月份从0开始，所以加1
  const day = String(date.getDate()).padStart(2, "0");
  const hours = String(date.getHours()).padStart(2, "0");
  const minutes = String(date.getMinutes()).padStart(2, "0");
  const seconds = String(date.getSeconds()).padStart(2, "0");

  return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
}

exports.SMScode = async (req, reply) => {
  try {
    const { phone } = req.body;
    if (!phone) {
      throw boom.badRequest("phone,code不能为空");
    }

    // 正则校验手机号是否正确
    const phoneReg = /^1[34578]\d{9}$/;
    if (!phoneReg.test(phone)) {
      throw boom.badRequest("手机号格式不正确");
    }

    // 发送一个6位数随机验证码,可以0开头
    const randomCode = generateRandomCode();
    console.log(randomCode);

    // 验证码发送成功后,需要设置过期时间
    const codeExpiresAtTimestamp = Date.now() + 5 * 60 * 1000;

    // const codeExpiresAtTimestamp = Date.now() + 10000;

    // 保存到数据库
    await User.findOneAndUpdate({ phone }, { code: randomCode, codeExpiresAt: codeExpiresAtTimestamp });

    return reply.send({
      code: randomCode,
      expiresAt: ` 验证码将在 ${timestampToDate(codeExpiresAtTimestamp)} 失效`,
    });
  } catch (error) {
    throw boom.boomify(error);
  }
};

exports.Bundled = async (req, reply) => {
  try {
    const { phone, code, account, password } = req.body;
    if (!phone || !code || !account || !password) {
      throw boom.badRequest("phone,code,account,password不能为空");
    }

    // 正则校验手机号是否正确
    const phoneReg = /^1[34578]\d{9}$/;
    if (!phoneReg.test(phone)) {
      throw boom.badRequest("手机号格式不正确");
    }

    // 校验手机号
    const user = await User.findOne({ phone });
    if (!user) {
      throw boom.badRequest("手机号不存在");
    }

    // 校验验证码
    if (user.code !== code) {
      throw boom.badRequest("验证码错误");
    }

    // 校验验证码是否过期
    if (user.codeExpiresAt < Date.now()) {
      throw boom.badRequest("验证码已过期");
    }
    // 校验用户名是否存在
    const userExist = await User.findOne({ account });
    if (userExist) {
      throw boom.badRequest("用户名已存在");
    }

    const { headers } = req;
    const existingToken = await JWTToken.findOne({ token: headers.token });
    if (!existingToken) {
      throw boom.unauthorized("Token 无效");
    }

    const hashedPassword = await bcrypt.hash(password, 10); // 第二个参数是 salt 的工作因子

    const tokenData = await verifyToken(headers.token);
    console.log(tokenData);

    if (tokenData.phone != phone) {
      throw boom.unauthorized("手机号不一致");
    }

    await User.findOneAndUpdate({ phone: user.phone }, { account, password: hashedPassword, code: "", codeExpiresAt: "" }, { new: true });
    return reply.send({
      code: 200,
      msg: "绑定成功",
    });
  } catch (error) {
    throw boom.boomify(error);
  }
};

exports.UserInfo = async (req, reply) => {
  try {
    const { headers } = req;
    const existingToken = await JWTToken.findOne({ token: headers.token });
    if (!existingToken) {
      throw boom.unauthorized("Token 无效");
    }

    const tokenData = await verifyToken(headers.token);
    const { iat, exp, ...filteredTokenData } = tokenData;

    return reply.send({
      code: 200,
      msg: "获取成功",
      data: {
        userInfo: filteredTokenData,
      },
    });
  } catch (error) {
    throw boom.boomify(error);
  }
};

exports.RoleInfoPOST = async (req, reply) => {
  try {
    const { headers } = req;
    const existingToken = await JWTToken.findOne({ token: headers.token });
    if (!existingToken) {
      throw boom.unauthorized("Token 无效");
    }

    const Data1 = await verifyToken(headers.token);
    console.log("shannnon", Data1);

    const { name, description, tag, level, permissions } = req.body;
    if (!name || !description || !tag || !level || !permissions) {
      throw boom.badRequest("name,description,tag不能为空");
    }

    const roleName = await Role.findOne({ name });
    if (roleName) {
      throw boom.badRequest("角色已存在");
    }

    const roleTag = await Role.findOne({ tag });
    if (roleTag) {
      throw boom.badRequest("角色tag已存在");
    }

    const roleLevel = await Role.findOne({ level });
    if (roleLevel) {
      throw boom.badRequest("角色level已存在");
    }

    let postbody = {
      name: req.body.name,
      description: req.body.description,
      tag: req.body.tag,
      level: req.body.level,
      permissions: req.body.permissions,
    };
    const role = new Role(postbody);
    return role.save();
  } catch (error) {
    throw boom.boomify(error);
  }
};

exports.RoleInfoDELETE = async (req, reply) => {
  try {
    const { headers, params } = req;
    const existingToken = await JWTToken.findOne({ token: headers.token });
    if (!existingToken) {
      throw boom.unauthorized("Token 无效");
    }

    const Data1 = await verifyToken(headers.token);
    console.log("shannnon", Data1);

    await Role.findByIdAndDelete(params.roleId);
    await User.updateMany({ roleId: params.roleId }, { $set: { roleId: "" } });
    return reply.send({
      code: 200,
      msg: "删除成功",
    });
  } catch (error) {
    throw boom.boomify(error);
  }
};

exports.RoleInfoPUT = async (req, reply) => {
  try {
    const { headers } = req;
    const existingToken = await JWTToken.findOne({ token: headers.token });
    if (!existingToken) {
      throw boom.unauthorized("Token 无效");
    }

    const Data1 = await verifyToken(headers.token);
    console.log("shannnon", Data1);

    const { name, description, tag, level, permissions } = req.body;
    if (!name || !description || !tag || !level || !permissions) {
      throw boom.badRequest("name,description,tag不能为空");
    }

    const roleName = await Role.findOne({ name });
    if (roleName) {
      throw boom.badRequest("角色已存在");
    }

    const roleTag = await Role.findOne({ tag });
    if (roleTag) {
      throw boom.badRequest("角色tag已存在");
    }

    const roleLevel = await Role.findOne({ level });
    if (roleLevel) {
      throw boom.badRequest("角色level已存在");
    }

    let postbody = {
      name: req.body.name,
      description: req.body.description,
      tag: req.body.tag,
      level: req.body.level,
      permissions: req.body.permissions,
    };

    await Role.findByIdAndUpdate(req.params.roleId, postbody, { new: true });
    return reply.send({
      code: 200,
      msg: "更新成功",
    });
  } catch (error) {
    throw boom.boomify(error);
  }
};

exports.RoleInfoGET = async (req, reply) => {
  try {
    const { headers } = req;
    const existingToken = await JWTToken.findOne({ token: headers.token });
    if (!existingToken) {
      throw boom.unauthorized("Token 无效");
    }

    const Data1 = await verifyToken(headers.token);
    console.log("shannnon", Data1);

    const { page = 1, pageSize = 10, name } = req.query;
    const queryConditions = {};
    if (name) queryConditions.name = new RegExp(name, "i"); // 添加模糊查询，忽略大小写

    const skip = (parseInt(page) - 1) * parseInt(pageSize);

    const Count = await Role.countDocuments(queryConditions);
    const Data = await Role.find(queryConditions).skip(skip).limit(parseInt(pageSize));

    let permissions = Data.map((item) => item.permissions);
    console.log(
      "shannnon",
      permissions.map((item) => ({
        label: item.label,
        method: item.method,
        url: item.url,
      }))
    );

    // 构造一个没有 _id 的数据数组
    // const DataWithoutId = Data.permissions.map((item) => ({
    //   label: item.label,
    //   method: item.method,
    //   url: item.url,
    // }));

    const responseData = {
      count: Count,
      data: Data,
    };

    return responseData;
  } catch (error) {
    throw boom.boomify(err);
  }
};

exports.Allapi = async (req, reply) => {
  try {
    const { headers } = req;
    const existingToken = await JWTToken.findOne({ token: headers.token });
    if (!existingToken) {
      throw boom.unauthorized("Token 无效");
    }

    const Data1 = await verifyToken(headers.token);
    console.log("shannnon", Data1);

    const { page, pageSize, name, path, tags } = req.query;
    const queryConditions = {};
    if (name) queryConditions.summary = new RegExp(name, "i");
    if (path) queryConditions.path = new RegExp(path, "i");
    if (tags) queryConditions.tags = new RegExp(tags, "i");
    const skip = (parseInt(page) - 1) * parseInt(pageSize);
    const Count = await Api.countDocuments(queryConditions);
    const Data = await Api.find(queryConditions).skip(skip).limit(parseInt(pageSize));

    // 构造一个没有 _id 的数据数组
    const DataWithoutId = Data.map((item) => ({
      path: item.path,
      tags: item.tags,
      method: item.method,
      name: item.name,
      summary: item._doc.summary,
    }));

    const responseData = {
      count: Count,
      data: DataWithoutId,
    };

    return reply.send(responseData);
  } catch (error) {
    throw boom.boomify(error);
  }
};

exports.Bindrole = async (req, reply) => {
  try {
    const { headers } = req;
    const existingToken = await JWTToken.findOne({ token: headers.token });
    if (!existingToken) {
      throw boom.unauthorized("Token 无效");
    }

    const Data1 = await verifyToken(headers.token);
    console.log("shannnon", Data1);

    const { userId, roleId } = req.body;
    if (!roleId) throw boom.badRequest("缺少参数");
    if (!userId) throw boom.badRequest("缺少参数");

    const existingUser = await User.findById(userId);
    if (!existingUser) {
      throw boom.notFound("用户不存在");
    }

    console.log("existingUser", existingUser);

    if (existingUser && existingUser.roleId === roleId) {
      return reply.send({ message: "用户已经绑定了该角色" });
    }

    // 获取旧角色的信息
    const oldRoleId = existingUser.roleId;
    let oldRole = null;

    if (oldRoleId) {
      // 如果用户有旧角色，则先减少旧角色的 userCount
      oldRole = await Role.findById(oldRoleId);
      if (oldRole) {
        await Role.findOneAndUpdate({ _id: oldRoleId }, { $inc: { userCount: -1 } }, { new: true });
      }
    }

    // 绑定新角色并增加新角色的 userCount
    await User.findOneAndUpdate({ _id: userId }, { roleId }, { new: true });

    // 获取新角色的信息
    const newRole = await Role.findById(roleId);
    if (newRole) {
      await Role.findOneAndUpdate({ _id: roleId }, { $inc: { userCount: 1 } }, { new: true });
    }

    return reply.send({ message: "绑定成功" });
  } catch (error) {
    throw boom.boomify(err);
  }
};

exports.QrcodeInfo = async (req, reply) => {};

exports.QrcodeInfoPOST = async (req, reply) => {
  try {
    const { name, height, weight, gender, telephone } = req.body;
    if (!name || !height || !weight || !gender || !telephone) {
      throw boom.badRequest("参数错误");
    }

    if (gender != 1 && gender != 0) {
      throw boom.badRequest("性别参数错误");
    }

    if (!req.file) {
      throw boom.badRequest("至少需要上传一个文件");
    }

    const originalname = req.file.filename;
    const photoUrls = `${req.protocol}://${req.headers.host}/public/images/${originalname}`;

    const Data = new Person({
      name,
      height,
      weight,
      gender,
      telephone,
      photoUrls,
    });

    // telephone 是唯一的索引，不允许重复
    const existingPerson = await Person.findOne({ telephone });
    if (existingPerson) {
      throw boom.badRequest("该用户已存在");
    }

    const person = await Data.save();

    const qrCodeDataURL = await Qrcode.toDataURL(`http://192.168.1.173:5500/public/index.html?id=${person._id}`);

    // 解析 base64 图像数据
    const base64Data = qrCodeDataURL.replace(/^data:image\/png;base64,/, "");
    const qrCodeImageBuffer = Buffer.from(base64Data, "base64");

    // 定义文件路径
    const qrCodeImagePath = path.join(__dirname, "../../public/images", `Qrcode-${new Date().getTime()}.png`);

    const filename = qrCodeImagePath.split("\\").pop();

    // 将 QR Code 图像保存到文件系统
    fs.writeFile(qrCodeImagePath, qrCodeImageBuffer, async (err) => {
      if (err) {
        // 文件保存失败,就删除 Person 文档
        await Person.findByIdAndDelete(person._id);
        return reply.send({ message: "添加失败" });
      }
      // 文件保存成功后，更新 Person 文档
      await Person.findByIdAndUpdate(person._id, { filePath: `${req.protocol}://${req.headers.host}/public/images/${filename}` });
    });

    reply.send({ message: "添加成功" });
  } catch (error) {
    throw boom.boomify(error);
  }
};

exports.QrcodeInfoID = async (req, reply) => {
  try {
    const id = req.params.id;
    const Data = await Person.findById(id);
    if (!Data) {
      throw boom.badRequest("null");
    }
    return reply.send(Data);
  } catch (err) {
    throw boom.boomify(err);
  }
};
